#include "XVideoWindow.h"
XVideoWindow::XVideoWindow(QWidget* parent)
	: QOpenGLWidget(parent)
{
	this->resize(QSize(480, 480));
	this->setWindowTitle("读取yuv数据并用OpenGL渲染");
	this->setWindowIcon(QIcon("images/opencv.png"));

	QPushButton* btnMuxer = new QPushButton(this);
	btnMuxer->setText("开始播放YUV数据");
	connect(btnMuxer, &QPushButton::clicked, [=]() {
		//启动定时器
		QTimer* ti = new QTimer(this);
		connect(ti, SIGNAL(timeout()), this, SLOT(update()));
		ti->start(40);
		});

	
}

void XVideoWindow::initializeGL() {
	this->initializeOpenGLFunctions();
	//清屏
	glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	float vertices[] = {
		//     ---- 位置 ----          - 纹理坐标 -
			 0.5f,  0.5f, 0.0f,   1.0f, 0.0f,   // 右上
			 0.5f, -0.5f, 0.0f,   1.0f, 1.0f,   // 右下
			-0.5f, -0.5f, 0.0f,   0.0f, 1.0f,   // 左下
			-0.5f,  0.5f, 0.0f,    0.0f, 0.0f    // 左上
	};
	unsigned int indices[] = {
	   0, 1, 3, // first triangle
	   1, 2, 3  // second triangle
	};
	//创建VAO
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	glGenBuffers(1, &EBO);
	glBindVertexArray(VAO);

	//创建VBO
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	//创建EBO
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

	//告知显卡如何解析顶点数据
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);

	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(1);


	//glBindVertexArray(0);
	//glBindBuffer(GL_ARRAY_BUFFER, 0);


	//小程序
	programId = buildAttachShaderAndReturnProgramId(":/QtForOpenCV4Tool/shader/yuv_texture_vert.glsl",
		":/QtForOpenCV4Tool/shader/yuv_texture_frag.glsl");

	glGenTextures(3, textures);
	glBindTexture(GL_TEXTURE_2D, textures[0]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	glBindTexture(GL_TEXTURE_2D, textures[1]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	glBindTexture(GL_TEXTURE_2D, textures[2]);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	glBindTexture(GL_TEXTURE_2D, 0);
	glBindTexture(GL_TEXTURE_2D, 1);
	glBindTexture(GL_TEXTURE_2D, 2);
	///分配材质内存空间
	datas[0] = new unsigned char[mVideoWidth * mVideoHeight];		//Y
	datas[1] = new unsigned char[mVideoWidth * mVideoHeight / 4];	//U
	datas[2] = new unsigned char[mVideoWidth * mVideoHeight / 4];	//V

	fp = fopen("E:/tony/demo/visualstudio_workspace/QtForOpenCV4Tool/QtForOpenCV4Tool/bin/media/400_300_25.yuv", "rb");
	if (!fp)
	{
		qDebug() << "out240x128.yuv file open failed!";
	}


}
void XVideoWindow::resizeGL(int w, int h) {
	glViewport(0,0,w, h);
	setOrthoMProjection((float)240, (float)128);
}
void XVideoWindow::paintGL() {
	if (feof(fp))
	{
		fseek(fp, 0, SEEK_SET);
	}
	fread(datas[0], 1, mVideoWidth * mVideoHeight, fp);
	fread(datas[1], 1, mVideoWidth * mVideoHeight / 4, fp);
	fread(datas[2], 1, mVideoWidth * mVideoHeight / 4, fp);


	glBindVertexArray(VAO);
	glUseProgram(programId);
	uModelMatrix.setToIdentity();//设置成为单位矩阵
	glUniformMatrix4fv(glGetUniformLocation(programId, "uni_mat"), 1, GL_FALSE, uProjectionMatrix.data());

	//qDebug() <<"buf[0]=" << sizeof(buf[0]) << ",buf[1]=" << sizeof(buf[1]) << ",buf[2]=" << sizeof(buf[2]);

	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, textures[0]);
	glUniform1i(glGetUniformLocation(programId, "uni_textureY"), 0);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, mVideoWidth, mVideoHeight, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, datas[0]);

	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, textures[1]);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, mVideoWidth / 2, mVideoHeight / 2, 0,
		GL_LUMINANCE, GL_UNSIGNED_BYTE, datas[1]);
	glUniform1i(glGetUniformLocation(programId, "uni_textureU"), 1);
	

	glActiveTexture(GL_TEXTURE2);
	glBindTexture(GL_TEXTURE_2D, textures[2]);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, mVideoWidth / 2, mVideoHeight / 2, 0,
		GL_LUMINANCE, GL_UNSIGNED_BYTE, datas[2]);
	glUniform1i(glGetUniformLocation(programId, "uni_textureV"), 2);
	
	glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);


	//解绑
	glBindVertexArray(0);
	glBindTexture(GL_TEXTURE_2D, 0);
	glBindTexture(GL_TEXTURE_2D, 1);
	glBindTexture(GL_TEXTURE_2D, 2);
	//renderVideo();
	qDebug() << "paintGL()=>>>>>>>>>>>>>>>>>>>>";
}


//以下是shader相关的方法
GLuint XVideoWindow::buildAttachShaderAndReturnProgramId(QString vertexResPath, QString fragmentResPath) {
	//编译顶点着色器和片元着色器
	GLuint vertexShader = getShaderId(GL_VERTEX_SHADER, vertexResPath);
	GLuint fragmentShader = getShaderId(GL_FRAGMENT_SHADER, fragmentResPath);
	GLuint programId = glCreateProgram();
	glAttachShader(programId, vertexShader);
	glAttachShader(programId, fragmentShader);
	glLinkProgram(programId);
	getLinkProgramErrorInfo(programId);

	glDeleteShader(vertexShader);
	glDeleteShader(fragmentShader);
	return programId;
}
void XVideoWindow::getLinkProgramErrorInfo(GLuint programId) {
	int success;
	char infoLog[512];
	glGetProgramiv(programId, GL_LINK_STATUS, &success);
	if (!success) {
		glGetProgramInfoLog(programId, 512, NULL, infoLog);
		qDebug() << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog;
	}
}
GLuint XVideoWindow::getShaderId(GLenum shaderType, QString resPath) {
	//创建顶点着色器
	unsigned int shaderId = glCreateShader(shaderType);
	QFile vertexShaderFile(resPath);
	if (!vertexShaderFile.open(QIODevice::ReadOnly)) {
		qDebug() << "Cannot open vertex shader file for reading";
	}
	QString verQStr = vertexShaderFile.readAll();
	std::string verStdStr = verQStr.toStdString();
	const char* vertexStr = verStdStr.c_str();
	qDebug() << "vertexStr-------------" << vertexStr;
	vertexShaderFile.flush();
	vertexShaderFile.close();
	glShaderSource(shaderId, 1, &vertexStr, NULL);
	glCompileShader(shaderId);
	getCompileShaderErrorInfo(shaderId);
	return shaderId;
}
void XVideoWindow::getCompileShaderErrorInfo(GLuint shaderId) {
	int success;
	char infoLog[512];
	glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		glGetShaderInfoLog(shaderId, 512, NULL, infoLog);
		qDebug() << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog;
	}
}
/*
* 正交投影算法，防止图片变形
*/
void XVideoWindow::setOrthoMProjection(float mWidth, float mHeight) {
	uProjectionMatrix.setToIdentity();//设置成为单位矩阵
	float mViewScale = (float)width() / (float)height();
	float mImgScale = mWidth / mHeight;
	float aspectRatio = 0.0f;
	if (mImgScale > mViewScale) {
		aspectRatio = mImgScale / mViewScale;
		uProjectionMatrix.ortho(-1.0f, 1.0f, -aspectRatio, aspectRatio, -1.0f, 1.0f);
	}
	else {
		aspectRatio = mViewScale / mImgScale;
		uProjectionMatrix.ortho(-aspectRatio, aspectRatio, -1.0f, 1.0f, -1.0f, 1.0f);
	}
}

XVideoWindow::~XVideoWindow()
{
}
